/*
 * Created on 4. sep.. 2006
 * Created by Tomas
 */
package people;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Iterator;
import java.util.List;

import org.junit.Before;
import org.junit.Test;

import util.Debug;

public class MembershipManagerTest {
	private MembershipManager bafta;
	private Person rowan, hugh, stephen, tony, tim;
	private Group blackAdder, fryAndLaurie, funnyMen, queer;
	
	private int blackAdderScore 	= 30;
	private int fryAndLaurieScore 	= 25;
	private int funnyScore 			= 18;
	private int queerScore 			= 100;
	
	@Before
	public void setUp() throws Exception {
		bafta = new MembershipManager();
		
		rowan 	= new Person(1, "Rowan", "Atkinson");
		hugh 	= new Person(2, "Hugh", "Laurie");
		stephen = new Person(3, "Stephen", "Fry");
		tony 	= new Person(4, "Tony", "Robinson");
		tim 	= new Person(5, "Tim", "McInnerny");
		
		blackAdder 		= new Group("Black Adder", blackAdderScore);
		fryAndLaurie	= new Group("A Bit of Fry And Laurie", fryAndLaurieScore);
		funnyMen		= new Group("Funny Men", funnyScore);
		queer 			= new Group("Queer and Here", queerScore);
		
		blackAdder.setScore(blackAdderScore);
		fryAndLaurie.setScore(fryAndLaurieScore);
		funnyMen.setScore(funnyScore);
		queer.setScore(queerScore);
	}

	/*
	 * The adding of Persons
	 *
	 */
	@Test
	public final void testAddPerson() {
		if (Debug.ON) {
			System.out.println("\n\t-testAddPerson-");
		}
		assertTrue(bafta.getPersons().isEmpty());
		
		//Add a person, and check that he's there
		try {
			bafta.addPerson(rowan);
		} catch (DuplicatePersonException e2) {
			fail("Mr Atkinson was managed before I knew it!");
		}
		assertFalse(bafta.getPersons().isEmpty());
		try {
			assertEquals(rowan, bafta.getPerson(rowan.getID()));
		} catch (PersonNotFoundException e) {
			fail();
		}
		try {
			assertEquals(rowan, bafta.getPerson(hugh.getID()));
			fail();
		} catch (PersonNotFoundException e) {
			//this should happen
		}
		
		//add a few more people, and check that they get there
		try {
			bafta.addPerson(hugh);
			bafta.addPerson(stephen);
			bafta.addPerson(tony);
		} catch (DuplicatePersonException e1) {
			fail("Comedians were already there");
		}
		
		try {
			assertEquals(hugh, bafta.getPerson(hugh.getID()));
			assertEquals(stephen, bafta.getPerson(stephen.getID()));
			assertEquals(tony, bafta.getPerson(tony.getID()));
		}
		catch (PersonNotFoundException e) {
			// Shouldn't happen
			fail();
		}
		
		
	}

	/*
	 * The adding of Groups
	 */
	@Test
	public final void testAddGroup() {
		if (Debug.ON) {
			System.out.println("\n\t-testAddGroup-");
		}
		assertTrue(bafta.getGroups().isEmpty());
		
		bafta.addGroup(funnyMen);
		assertFalse(bafta.getGroups().isEmpty());
		assertTrue(bafta.getGroups().contains(funnyMen));
		
		//add a few more
		bafta.addGroup(blackAdder);
		bafta.addGroup(fryAndLaurie);
		
		//check that they're there
		List<Group> groups = bafta.getGroups();
		assertTrue(groups.contains(funnyMen));
		assertTrue(groups.contains(blackAdder));
		assertTrue(groups.contains(fryAndLaurie));
		assertFalse(groups.contains(queer));
	}

	/*
	 * The adding of Groups with people in them 
	 */
	@Test
	public final void testAddFullGroups() {
//		Debug.ON = true;
		if (Debug.ON) {
			System.out.println("\n\t-testAddFullGroups-");
		}
		fryAndLaurie.addPerson(hugh);
		fryAndLaurie.addPerson(stephen);
		
		blackAdder.addPerson(rowan);
		blackAdder.addPerson(hugh);
		blackAdder.addPerson(stephen);
		blackAdder.addPerson(tony);
		blackAdder.addPerson(tim);
		
		funnyMen.addPerson(rowan);
		funnyMen.addPerson(hugh);
		funnyMen.addPerson(stephen);
		funnyMen.addPerson(tony);
		funnyMen.addPerson(tim);
		
		assertTrue(bafta.getGroups().isEmpty());
		
		bafta.addGroup(fryAndLaurie);
		assertTrue(bafta.getGroups().contains(fryAndLaurie));
		try {
			assertEquals(hugh, bafta.getPerson(hugh.getID()));
			assertEquals(stephen, bafta.getPerson(stephen.getID()));
		} catch (PersonNotFoundException e) {
			//shouldn't happen
			fail();
		}
		try {
			//should fail
			assertEquals(rowan, bafta.getPerson(rowan.getID()));
			fail();
		} catch (PersonNotFoundException e) {
			// This should happen
		}
		
		/*
		 * A small experiment - if we add Rowan to the Group, he's not yet
		 * known by Bafta. After Bafta re-rates it's Groups and Persons, however
		 * he's known. This is as intended.
		 */
		fryAndLaurie.addPerson(rowan);
		try {
			//should fail
			assertEquals(rowan, bafta.getPerson(rowan.getID()));
			fail();
		} catch (PersonNotFoundException e) {
			// This should happen
		}
		bafta.reRatePersons();
		try {
			//now bafta will find rowan!
			assertEquals(rowan, bafta.getPerson(rowan.getID()));
		} catch (PersonNotFoundException e) {
			// This shouldn't happen
			fail();
		}
		
		//group with all
		bafta.addGroup(blackAdder);
		assertTrue(bafta.getGroups().contains(blackAdder));
		try {
			assertEquals(tony, bafta.getPerson(tony.getID()));
			assertEquals(tim, bafta.getPerson(tim.getID()));
		} catch (PersonNotFoundException e) {
			// shouldn't happen
			fail();
		}
		
		//another group with all, but it has not been added
		assertFalse(bafta.getGroups().contains(funnyMen));
		Debug.ON = false;
		
	}

	@Test
	public final void testPersonsInGroupsThroughManager() {
		if (Debug.ON) {
			System.out.println("\n\t-testPersonsInGroupsThroughManager-");
		}
		assertTrue(bafta.getGroups().isEmpty());
		assertTrue(bafta.getPersons().isEmpty());
		
		bafta.addGroup(fryAndLaurie);
		bafta.addGroup(funnyMen);
		blackAdder.addPerson(rowan);
		blackAdder.addPerson(tony);
		
		try {
			bafta.addPerson(rowan);
			bafta.addPerson(hugh);
			bafta.addPerson(stephen);
		} catch (DuplicatePersonException e1) {
			fail("People were managed before they could be. Zounds!");
		}
		assertFalse(bafta.getGroups().contains(blackAdder));
		
		
		bafta.addPersonToGroup(hugh, fryAndLaurie);
		bafta.addPersonToGroup(stephen, fryAndLaurie);
		
		int jw = bafta.getGroups().indexOf(fryAndLaurie);
		Group shouldBeJAndW = bafta.getGroups().get(jw);
		
		assertEquals(fryAndLaurie, shouldBeJAndW);
		
		assertEquals(fryAndLaurie.getPersons().indexOf(hugh),
				shouldBeJAndW.getPersons().indexOf(hugh));
		assertEquals(fryAndLaurie.getPersons().indexOf(stephen),
				shouldBeJAndW.getPersons().indexOf(stephen));
		assertFalse(shouldBeJAndW.getPersons().contains(rowan));
		
		//try adding to a group that's not yet known
		bafta.addPersonToGroup(rowan, blackAdder);
		
		assertTrue(bafta.getGroups().contains(blackAdder));
		try {
			// Tony was part of the Group added, he's now known
			assertEquals(tony, bafta.getPerson(tony.getID()));
		} catch (PersonNotFoundException e) {
			//Should not happen
			fail();
		}
	}
	
	/*
	 * Test that the scoring of Persons according to their Group memberships work
	 */
	@Test
	public final void testScoringPersons() {
//		Debug.ON = true;
		if (Debug.ON) {
			System.out.println("\n\t-testScoringPersons-");
		}
		assertEquals(0, rowan.getScore());
		assertEquals(0, hugh.getScore());
		assertEquals(0, stephen.getScore());
		
		bafta.addGroup(fryAndLaurie);
		bafta.addPersonToGroup(hugh, fryAndLaurie);
		bafta.addPersonToGroup(stephen, fryAndLaurie);
		
		bafta.addPersonToGroup(rowan, funnyMen);
		bafta.addPersonToGroup(hugh, funnyMen);
		
		assertEquals(funnyScore, rowan.getScore());
		assertEquals(fryAndLaurieScore, stephen.getScore());
		assertEquals(fryAndLaurieScore + funnyScore, hugh.getScore());
		
		bafta.getPersons();
		
		Debug.ON = false;
		
	}
	
	@Test
	public final void testRemovalScoreDecrease() {
		if (Debug.ON) {
			System.out.println("\n\t-testRemovalScoreDecrease-");
		}
		testScoringPersons();

		bafta.removePersonFromGroup(hugh, funnyMen);
		assertEquals(fryAndLaurieScore, hugh.getScore());
		
		bafta.removePersonFromGroup(hugh, funnyMen); //not there anymore

		assertEquals(fryAndLaurieScore, hugh.getScore()); //nothing changes
	}

	@Test
	public final void testScoringOfSameGroup() {
		if (Debug.ON) {
			System.out.println("\n\t-testScoringOfSameGroup-");
		}
		testRemovalScoreDecrease();
		assertEquals(fryAndLaurieScore, hugh.getScore());
		bafta.addPersonToGroup(hugh, fryAndLaurie);
		assertEquals(fryAndLaurieScore, hugh.getScore());
	}


	@Test
	public final void testReRatePersons() {
		if (Debug.ON) {
			System.out.println("\n\t-testReRatePersons-");
		}
		testScoringPersons();

		assertTrue(funnyScore == rowan.getScore());
		assertTrue(fryAndLaurieScore == stephen.getScore());
		
		rowan.addToScore(44);
		blackAdder.addPerson(stephen);
		
		assertFalse(funnyScore == rowan.getScore());
		assertFalse(fryAndLaurieScore == stephen.getScore());
		
		bafta.reRatePersons();
		if (Debug.ON) {
			System.out.println("These should be equal: " + funnyScore + "==" + rowan.getScore());
		}
		assertTrue(funnyScore == rowan.getScore());
		assertTrue(fryAndLaurieScore == stephen.getScore());
	}
	
	@Test
	public final void testQueuingPersons() {
//		Debug.ON = true;
		if (Debug.ON) {
			System.out.println("\n\t-testQueuingPersons-");
		}
		bafta.addPersonToGroup(hugh, fryAndLaurie);
		bafta.addPersonToGroup(hugh, blackAdder);
		bafta.addPersonToGroup(tony, blackAdder);
		bafta.addPersonToGroup(rowan, blackAdder);
		bafta.addPersonToGroup(stephen, blackAdder);
		bafta.addPersonToGroup(tim, blackAdder);
		
		bafta.addPersonToGroup(stephen, fryAndLaurie);
		
		bafta.addPersonToGroup(rowan, funnyMen);
		bafta.addPersonToGroup(hugh, funnyMen);
		bafta.addPersonToGroup(stephen, funnyMen);
		
		bafta.addPersonToGroup(stephen, queer);
		
		List<Person> ordering = bafta.getPersons();
		
		
		if (Debug.ON) {
			Iterator<Person> it = ordering.iterator();
			Person previous = it.next();
			while (it.hasNext()) {
				Person current = it.next();
				System.out.println(previous.getName() + " to " + current.getName() 
						+ ": " + previous.compareTo(current));
				previous = current;
			}
		}
		
		assertEquals(blackAdderScore + funnyScore, rowan.getScore());
		assertEquals(blackAdderScore + fryAndLaurieScore + funnyScore, hugh.getScore());
		assertEquals(blackAdderScore + fryAndLaurieScore + funnyScore + queerScore, stephen.getScore());
		
		
		
		Debug.ON = false;
	}

}
